#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/2/19 14:17
# @Author  : YueXia
# @File    : exception_handler.py
# @Description :全局异常处理
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import ORJSONResponse, RedirectResponse
from pydantic import ValidationError
from pydantic.errors import PydanticUserError
from starlette.exceptions import HTTPException as StarletteHTTPException
from tortoise.exceptions import IntegrityError

from applications import settings
from applications.common.render_template import templates
from applications.common.user_manager import NotAuthenticatedException
from applications.core.context_manager.track_utils import TrackContextUtils
from applications.core.exceptions import PermissionDeniedException
from applications.log import logger


async def _validation_exception_handler(e: RequestValidationError | ValidationError):
    """
    数据验证异常处理

    :param e:
    :return:
    """
    # errors = []
    errors = e.errors()
    error = errors[0]
    if error.get("type") == "json_invalid":
        message = "json解析失败"
    else:
        error_input = error.get("input")
        field = str(error.get("loc")[-1])
        error_msg = error.get("msg")
        message = f"{field} {error_msg}，输入：{error_input}"
    msg = f"请求参数非法: {message}"
    # data = {'errors': errors} if settings.ENVIRONMENT == 'dev' else None
    return ORJSONResponse(
        status_code=200,
        content={"msg": msg, "success": False, "data": None},
        headers=get_headers(),
    )


def get_headers():
    return {"x-request-id": TrackContextUtils.get_request_id()}


def register_exception(app: FastAPI):
    @app.exception_handler(PermissionDeniedException)
    def permission_exception(request: Request, exc: PermissionDeniedException):
        """
        权限异常
        :param request:
        :param exc:
        :return:
        """
        logger.warning("权限异常")
        logger.warning(f"请求地址{request.url.path}")
        logger.error(exc.__str__())
        return templates.TemplateResponse("errors/403.html", {"request": request}, status_code=403)

    @app.exception_handler(NotAuthenticatedException)
    def token_err(request: Request, exc: NotAuthenticatedException):
        """
        token校验异常
        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址{request.url.path}")
        return RedirectResponse(
            url="/system/passport/login",
        )

    @app.exception_handler(IntegrityError)
    def app_err(request: Request, exc: IntegrityError):
        """
        自定义异常,比如。权限异常，用户不存在等。。。
        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址{request.url.path}")
        logger.warning("捕捉到重写AppException异常异常：custom_exception_handler")
        logger.error(exc.__str__())
        return ORJSONResponse(
            status_code=200,
            content={"msg": "数据库异常", "success": False, "data": None},
            headers=get_headers(),
        )

    @app.exception_handler(ValidationError)
    async def validation_error(request: Request, exc: ValidationError):
        """
        处理参数异常
        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址{request.url.path}")
        logger.error(exc.__str__())
        logger.error(exc.__str__())
        return await _validation_exception_handler(exc)

    @app.exception_handler(RequestValidationError)
    async def request_validation_error(request: Request, exc: RequestValidationError):
        """
        全局请求数据验证异常处理,处理请求参数验证的异常
        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址:{request.url.path}")
        logger.error(exc.errors())
        return await _validation_exception_handler(exc)

    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(request: Request, exc: StarletteHTTPException):
        """处理客户端请求异常
        code: 312 404
        """
        logger.warning(f"请求地址:{request.url.path}")
        # 获取状态码
        _status = exc.status_code
        msg = exc.detail
        logger.error(msg)
        if _status == 404:
            return templates.TemplateResponse("errors/404.html", {"request": request}, status_code=_status)

        return ORJSONResponse(
            status_code=200,
            content={"msg": msg, "success": False, "data": None},
            headers=get_headers(),
        )

    @app.exception_handler(PydanticUserError)
    async def pydantic_user_error_handler(request: Request, exc: PydanticUserError):
        """
        Pydantic 未正确使用导致的异常处理

        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址:{request.url.path}")
        logger.warning(f"请求参数:{exc.__str__()}")
        return ORJSONResponse(
            status_code=200,
            content={"msg": "服务器异常", "success": False, "data": None},
            headers=get_headers(),
        )

    @app.exception_handler(ValueError)
    async def value_exception_handler(request: Request, exc: ValueError):
        """
        捕获值异常
        """
        if settings.DEBUG:
            logger.warning(f"请求地址:{request.url.path}")
            logger.warning("捕捉到值异常：value_exception_handler")
            logger.warning(exc.__str__())
        # 打印栈信息，方便追踪排查异常
        logger.exception(exc)
        return ORJSONResponse(
            status_code=200,
            content={"msg": exc.__str__(), "success": False, "data": None},
        )

    @app.exception_handler(AssertionError)
    async def assertion_error_handler(request: Request, exc: AssertionError):
        """
        断言错误处理

        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址:{request.url.path}")
        logger.exception(exc)
        return ORJSONResponse(
            status_code=200,
            content={"msg": "服务器异常", "success": False, "data": None},
            headers=get_headers(),
        )

    @app.exception_handler(Exception)
    async def all_exception_handler(request: Request, exc: Exception):
        """
        全局异常处理
        :param request:
        :param exc:
        :return:
        """
        logger.warning(f"请求地址:{request.url.path}")
        logger.exception(exc)
        return ORJSONResponse(
            status_code=200,
            content={"msg": "服务器异常", "success": False, "data": None},
            headers=get_headers(),
        )
